// Learn0 project main.go
package main

import (
	"fmt"
	"math/rand"
	"sync"
)

/*
fallthrough：Go里面switch默认相当于每个case最后带有break，匹配成功后不会自动向下执行其他case，而是跳出整个switch, 但是可以使用fallthrough强制执行后面的case代码。
fallthrough不能用在switch的最后一个分支。
*/
func main() {
	fmt.Println("if switch for select defer error panic go")
	var number int = 5
	if number += 4; 10 > number {
		number := 36
		number += 3
		fmt.Println(number)
	} else if 10 < number {
		number -= 2
		fmt.Print(number)
	}
	fmt.Println(number)
	v := 11
	switch i := interface{}(v).(type) {
	case int, int8, int16, int32, int64:
		fmt.Printf("A signed integer: %d. The type is %T. \n", i, i)
	case uint, uint8, uint16, uint32, uint64:
		fmt.Printf("A unsigned integer: %d. The type is %T. \n", i, i)
	default:
		fmt.Println("Unknown!")
	}

	ia := []interface{}{byte(6), 'a', uint(10), int32(-4)}
	switch v := ia[rand.Intn(4):4]; interface{}(v).(type) {
	case []interface{}:
		fmt.Printf("Case A.")
	case byte:
		fmt.Printf("Case B.")
	default:
		fmt.Println("Unknown!")
	}
	fordemo1()
	selectdemo()
	deferdemo1()
	main0()
	fmt.Println("Enter main")
	/**
	其实只要在panic或其调用栈之前加上 defer ... recover语句就可以了
	*/
	outerFunc()
	fmt.Println("Quit main")

	main2()
	main3()
}

func fordemo1() {
	for i := 0; i < 10; i++ {
		fmt.Print(i, " ")
	}
	for i, v := range "Go语言" {
		fmt.Printf("%d: %c\n", i, v)
	}
	/**
	  对于数组值、数组的指针值和切片之来说，range子句每次也会迭代出两个值。其中，第一个值会是第二个值在被迭代值中的索引，而第二个值则是被迭代值中的某一个元素。同样的，迭代是以索引递增的顺序进行的。

	      对于字典值来说，range子句每次仍然会迭代出两个值。显然，第一个值是字典中的某一个键，而第二个值则是该键对应的那个值。注意，对字典值上的迭代，Go语言是不保证其顺序的。

	      携带range子句的for语句还可以应用于一个通道值之上。其作用是不断地从该通道值中接收数据，不过每次只会接收一个值。注意，如果通道值中没有数据，那么for语句的执行会处于阻塞状态。无论怎样，这样的循环会一直进行下去。直至该通道值被关闭，for语句的执行才会结束。

	      最后，我们来说一下break语句和continue语句。它们都可以被放置在for语句的代码块中。前者被执行时会使其所属的for语句的执行立即结束，而后者被执行时会使当次迭代被中止（当次迭代的后续语句会被忽略）而直接进入到下一次迭代。
	*/
	map1 := map[int]string{1: "Golang", 2: "Java", 3: "Python", 4: "C"}
	for b, c := range map1 {
		fmt.Printf("%d: %s\n", b, c)
	}
}

func selectdemo() {
	/*
			如果一条select语句中不存在default case， 并且在被执行时其中的所有case都不满足执行条件，那么它的执行将会被阻塞！当前流程的进行也会因此而停滞。直到其中一个case满足了执行条件，执行才会继续。我们一直在说case执行条件的满足与否取决于其操作的通道在当时的状态。这里特别强调一点，即：未被初始化的通道会使操作它的case永远满足不了执行条件。对于针对它的发送操作和接收操作来说都是如此。

		    最后提一句，break语句也可以被包含在select语句中的case语句中。它的作用是立即结束当前的select语句的执行，不论其所属的case语句中是否还有未被执行的语句。
	*/
	ch1 := make(chan int, 1)
	ch2 := make(chan int, 1)
	// 省略若干条语句
	select {
	case e1 := <-ch1:
		fmt.Printf("1th case is selected. e1=%v.\n", e1)
	case e2 := <-ch2:
		fmt.Printf("2th case is selected. e2=%v.\n", e2)
	default:
		fmt.Println("No data!")
	}
	ch3 := make(chan int, 100)
	// 省略若干条语句
	select {
	case ch3 <- 1:
		fmt.Printf("Sent %d\n", 1)
	case ch3 <- 2:
		fmt.Printf("Sent %d\n", 2)
	default:
		fmt.Println("Full channel!")
	}
	ch4 := make(chan int, 1)
	for i := 0; i < 4; i++ {
		select {
		case e, ok := <-ch4:
			if !ok {
				fmt.Println("End.")
				return
			}
			fmt.Println(e)
			close(ch4)
		default:
			fmt.Println("No Data!")
			ch4 <- 1
		}
	}

}

func deferdemo1() {
	/**
						与select语句一样，Go语言中的defer语句也非常独特，而且比前者有过之而无不及。defer语句仅能被放置在函数或方法中。它由关键字defer和一个调用表达式组成。注意，这里的调用表达式所表示的既不能是对Go语言内建函数的调用也不能是对Go语言标准库代码包unsafe中的那些函数的调用。实际上，满足上述条件的调用表达式被称为表达式语句。请看下面的示例：

					func readFile(path string) ([]byte, error) {
					    file, err := os.Open(path)
					    if err != nil {
					        return nil, err
					    }
					    defer file.Close()
					    return ioutil.ReadAll(file)
					}
					    函数readFile的功能是读出指定文件或目录（以下统称为文件）本身的内容并将其返回，同时当有错误发生时立即向调用方报告。其中，os和ioutil（导入路径是io/ioutil）代表的都是Go语言标准库中的代码包。请注意这个函数中的倒数第二条语句。我们在打开指定文件且未发现有错误发生之后，紧跟了一条defer语句。其中携带的表达式语句表示的是对被打开文件的关闭操作。注意，当这条defer语句被执行的时候，其中的这条表达式语句并不会被立即执行。它的确切的执行时机是在其所属的函数（这里是readFile）的执行即将结束的那个时刻。也就是说，在readFile函数真正结束执行的前一刻，file.Close()才会被执行。这也是defer语句被如此命名的原因。我们在结合上下文之后就可以看出，语句defer file.Close()的含义是在打开文件并读取其内容后及时地关闭它。该语句可以保证在readFile函数将结果返回给调用方之前，那个文件或目录一定会被关闭。这实际上是一种非常便捷和有效的保险措施。

					    更为关键的是，无论readFile函数正常地返回了结果还是由于在其执行期间有运行时恐慌发生而被剥夺了流程控制权，其中的file.Close()都会在该函数即将退出那一刻被执行。这就更进一步地保证了资源的及时释放。

					    注意，当一个函数中存在多个defer语句时，它们携带的表达式语句的执行顺序一定是它们的出现顺序的倒序。下面的示例可以很好的证明这一点：

					func deferIt() {
					    defer func() {
					        fmt.Print(1)
					    }()
					    defer func() {
					        fmt.Print(2)
					    }()
					    defer func() {
					        fmt.Print(3)
					    }()
					    fmt.Print(4)
					}
					    deferIt函数的执行会使标准输出上打印出4321。请大家猜测下面这个函数被执行时向标准输出打印的内容，并真正执行它以验证自己的猜测。最后论证一下自己的猜测为什么是对或者错的。

					func deferIt2() {
					    for i := 1; i < 5; i++ {
					        defer fmt.Print(i)
					    }
					}
					    最后，对于defer语句，我还有两个特别提示：

					    1. defer携带的表达式语句代表的是对某个函数或方法的调用。这个调用可能会有参数传入，比如：fmt.Print(i + 1)。如果代表传入参数的是一个表达式，那么在defer语句被执行的时候该表达式就会被求值了。注意，这与被携带的表达式语句的执行时机是不同的。请揣测下面这段代码的执行：

					func deferIt3() {
					    f := func(i int) int {
					        fmt.Printf("%d ",i)
					        return i * 10
					    }
					    for i := 1; i < 5; i++ {
					        defer fmt.Printf("%d ", f(i))
					    }
					}
					    它在被执行之后，标准输出上打印出1 2 3 4 40 30 20 10 。

					    2. 如果defer携带的表达式语句代表的是对匿名函数的调用，那么我们就一定要非常警惕。请看下面的示例：

					func deferIt4() {
					    for i := 1; i < 5; i++ {
					        defer func() {
					            fmt.Print(i)
					        }()
					    }
					}
					    deferIt4函数在被执行之后标出输出上会出现5555，而不是4321。原因是defer语句携带的表达式语句中的那个匿名函数包含了对外部（确切地说，是该defer语句之外）的变量的使用。注意，等到这个匿名函数要被执行（且会被执行4次）的时候，包含该defer语句的那条for语句已经执行完毕了。此时的变量i的值已经变为了5。因此该匿名函数中的打印函数只会打印出5。正确的用法是：把要使用的外部变量作为参数传入到匿名函数中。修正后的deferIt4函数如下：

					func deferIt4() {
					    for i := 1; i < 5; i++ {
					        defer func(n int) {
					            fmt.Print(n)
					        }(i)
					    }
					}


					example1

				func f() (result int) {
				    defer func() {
				        result++
				    }()
				    return 0
				}
				example2

				func f() (r int) {
				     t := 5
				     defer func() {
				       t = t + 5
				     }()
				     return t
				}
				example3

				func f() (r int) {
				    defer func(r int) {
				          r = r + 5
				    }(r)
				    return 1
				}


			A. 无名返回值的情况

			package main

			import (
				"fmt"
			)

			func main() {
				fmt.Println("return:", a()) // 打印结果为 return: 0
			}

			func a() int {
				var i int
				defer func() {
					i++
					fmt.Println("defer2:", i) // 打印结果为 defer: 2
				}()
				defer func() {
					i++
					fmt.Println("defer1:", i) // 打印结果为 defer: 1
				}()
				return i
			}


			B. 有名返回值的情况

			package main

			import (
				"fmt"
			)

			func main() {
				fmt.Println("return:", b()) // 打印结果为 return: 2
			}

			func b() (i int) {
				defer func() {
					i++
					fmt.Println("defer2:", i) // 打印结果为 defer: 2
				}()
				defer func() {
					i++
					fmt.Println("defer1:", i) // 打印结果为 defer: 1
				}()
				return i // 或者直接 return 效果相同
			}


			先来假设出结论，帮助大家理解原因：

			多个defer的执行顺序为“后进先出”；

			defer、return、返回值三者的执行逻辑应该是：return最先执行，return负责将结果写入返回值中；接着defer开始执行一些收尾工作；最后函数携带当前返回值退出。

			如何解释两种结果的不同：

			上面两段代码的返回结果之所以不同，其实从上面第2条结论很好理解。

			a()int 函数的返回值没有被提前声名，其值来自于其他变量的赋值，而defer中修改的也是其他变量，而非返回值本身，因此函数退出时返回值并没有被改变。

			b()(i int) 函数的返回值被提前声名，也就意味着defer中是可以调用到真实返回值的，因此defer在return赋值返回值 i 之后，再一次地修改了 i 的值，最终函数退出后的返回值才会是defer修改过的值。


		C. 下面我们再来看第三个例子，验证上面的结论：

		package main

		import (
			"fmt"
		)

		func main() {
			fmt.Println("c return:", *(c())) // 打印结果为 c return: 2
		}

		func c() *int {
			var i int
			defer func() {
				i++
				fmt.Println("c defer2:", i) // 打印结果为 c defer: 2
			}()
			defer func() {
				i++
				fmt.Println("c defer1:", i) // 打印结果为 c defer: 1
			}()
			return &i
		}


		虽然 c()*int 的返回值没有被提前声明，但是由于 c()*int 的返回值是指针变量，那么在return将变量 i 的地址赋给返回值后，defer再次修改了 i 在内存中的实际值，因此函数退出时返回值虽然依旧是原来的指针地址，但是其指向的内存实际值已经被成功修改了。


	i := 0

	defer fmt.Printf("%d ", i)

	i = 10

	defer fmt.Printf("%d ", i)



	输出结果：

	10 0

	执行第一个defer时，使用的值是0，而不是函数最后执行结束时变成的10。

	*/
	for i := 0; i < 10; i++ {
		//fmt.Printf("%d ", fibonacci(i))
		defer fmt.Printf("%d ", func(n int) int {
			fmt.Printf("%d ", n)
			return n
		}(fibonacci(i)))
	}
}
func fibonacci(num int) int {
	if num == 0 {
		return 0
	}
	if num < 2 {
		return 1
	}
	return fibonacci(num-1) + fibonacci(num-2)
}

/*
package main

import (
    "fmt"
    "time"
)

func main() {
    go fmt.Println("Go!")
    time.Sleep(100 * time.Millisecond)
}
    语句time.Sleep(100 * time.Millisecond)会把main函数的执行结束时间向后延迟100毫秒。100毫秒虽短暂，但足够go函数被调度执行的了。上述命令源码文件在被运行时会如我们所愿地在标准输出上打印出Go!。


    另一个比较绅士的做法是在main函数的最后调用runtime.Gosched函数。相应的程序版本如下：

package main

import (
    "fmt"
    "runtime"
)

func main() {
    go fmt.Println("Go!")
    runtime.Gosched()
}
   runtime.Gosched函数的作用是让当前正在运行的Goroutine（这里是运行main函数的那个Goroutine）暂时“休息”一下，而让Go运行时系统转去运行其它的Goroutine（这里是与go fmt.Println("Go!")对应并会封装fmt.Println("Go!")的那个Goroutine）。如此一来，我们就更加精细地控制了对几个Goroutine的运行的调度。

    当然，我们还有其它方法可以满足上述需求。并且，如果我们需要去左右更多的Goroutine的运行时机的话，下面这种方法也许更合适一些。请看代码：

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(3)
    go func() {
        fmt.Println("Go!")
        wg.Done()
    }()
    go func() {
        fmt.Println("Go!")
        wg.Done()
    }()
    go func() {
        fmt.Println("Go!")
        wg.Done()
    }()
    wg.Wait()
}
    sync.WaitGroup类型有三个方法可用——Add、Done和Wait。Add会使其所属值的一个内置整数得到相应增加，Done会使那个整数减1，而Wait方法会使当前Goroutine（这里是运行main函数的那个Goroutine）阻塞直到那个整数为0。这下你应该明白上面这个示例所采用的方法了。我们在main函数中启用了三个Goroutine来封装三个go函数。每个匿名函数的最后都调用了wg.Done方法，并以此表达当前的go函数会立即执行结束的情况。当这三个go函数都调用过wg.Done函数之后，处于main函数最后的那条wg.Wait()语句的阻塞作用将会消失，main函数的执行将立即结束。

*/
func main2() {

	var wg sync.WaitGroup

	wg.Add(1)

	go func() {

		fmt.Println("1")

		wg.Done()

	}()

	wg.Wait()

	wg.Add(1)

	go func() {

		fmt.Println("2")

		wg.Done()

	}()

	wg.Wait()

	wg.Add(1)

	go func() {

		fmt.Println("3")

		wg.Done()

	}()

	wg.Wait()

}

func main3() {
	ch1 := make(chan int, 1)
	ch2 := make(chan int, 1)
	ch3 := make(chan int, 1)
	go func() {
		fmt.Println("1")
		ch1 <- 1
	}()
	go func() {
		<-ch1
		fmt.Println("2")
		ch2 <- 2
	}()
	go func() {
		<-ch2
		fmt.Println("3")
		ch3 <- 3
	}()
	<-ch3
}
